package pe.com.trestperu.common.model.dao.impl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.util.ReflectHelper;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Text;
import org.jdom.input.SAXBuilder;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;

import pe.com.trestperu.common.exception.CoreDBException;
import pe.com.trestperu.common.model.bean.GenericBean;
import pe.com.trestperu.common.model.dao.GenericDAO;
import pe.com.trestperu.common.util.ReflectionUtils;
import pe.com.trestperu.common.util.Utils;

/**
 * GenericDAO Interface to access to Database
 *
 * @author Telly Tataje
 *
 */
@Repository
public class GenericDAOImpl implements GenericDAO {

	private static final Log log = LogFactory.getLog(GenericDAOImpl.class);
	
	public static final int RESULT_VOID = 2;

	public static final int RESULT_LIST = 1;
	
	public static final int RESULT_MAP = 3;

	public static final int RESULT_OBJECT = 0;

	private EntityManager entityManager;

	private List<String> xmlFiles;

	private Map<String, String> sqlQueryMap = new HashMap<String, String>();

	public GenericDAOImpl(List<String> xmlFiles){
		this.xmlFiles = xmlFiles;
		if(CollectionUtils.isNotEmpty(getXmlFiles())){
			SAXBuilder builder = new SAXBuilder();
			for (String xml : getXmlFiles()) {
				try {
					Document doc = builder.build(ResourceUtils.getFile(StringUtils.replace(xml, ".", "/").concat(".xml")));
					Element root = doc.getRootElement();
					List<?> sqlQueryList = root.getChildren("sql-query");
					if(CollectionUtils.isNotEmpty(sqlQueryList)){
						for(Object query : sqlQueryList){
							Element q = (Element)query;
							String name = q.getAttribute("name").getValue();
							String value = ((Text)q.getContent().get(0)).getText();
							sqlQueryMap.put(name, StringUtils.trim(value));
						}
					}
				} catch (FileNotFoundException e) {
					log.error("FileNotFoundException :: " + e.getMessage());
				} catch (JDOMException e) {
					log.error("JDOMException :: " + e.getMessage());
				} catch (IOException e) {
					log.error("IOException :: " + e.getMessage());
				}
				
			}
		}
	}

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager){
		this.entityManager = entityManager;
	}
	
	public Connection getConnection() throws CoreDBException{
		Connection cnx = null;
		cnx = entityManager.unwrap(java.sql.Connection.class);
		return cnx;
	}

	@Transactional
	public void delete(Object persistentInstance) throws CoreDBException{
		log.debug("deleting instance");
		try {
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			session.delete(persistentInstance);
			log.debug("delete successful");
		} catch (Exception e) {
			log.error("delete failed", e);
			throw new CoreDBException(e);
		}
	}

	@Transactional
	public void deleteById(Class<?> clazz, Serializable id) throws CoreDBException{
		log.debug("getting instance with id: " + id);
		try {
			Object object = this.findById(clazz, id);
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			session.delete(object);
			log.debug("delete by ID successful");
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	public Object findById(Class<?> clazz, Serializable id) throws CoreDBException{
		log.debug("getting instance with id: " + id);
		try {
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			Object instance = session.get(clazz, id);
			return instance;
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	public List<?> findByObject(Object object) throws CoreDBException{
		log.debug("getting instance with id: ");
		List<?> list;
		try {
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			Example customerExample = Example.create(object).excludeZeroes();
			Criteria criteria = session.createCriteria(object.getClass()).add(customerExample);
			list = criteria.list();
			return list;
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	public Date getCurrentDate() throws CoreDBException {
		String driver = null;
		try{
			driver = getConnection().getMetaData().getDriverName();
		}catch(Exception e){
			throw new CoreDBException(e);
		}
		if(StringUtils.isNotEmpty(driver)){
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			if(driver.indexOf("Oracle") != -1){
				org.hibernate.Query query = session.createSQLQuery("SELECT SYSDATE FROM DUAL");
				return (Date)query.uniqueResult();
			}
			if(driver.indexOf("MySql") != -1){
				org.hibernate.Query query = session.createSQLQuery("SELECT CURDATE()");
				return (Date)query.uniqueResult();
			}
		}
		return null;
	}

	public List<?> getFindCriteria(Object object) throws CoreDBException {
		this.setNull(object);
		List<?> list = null;

		try {
			Criteria c = getCriteria(object);
			list = c.list();

		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		} 
		log.debug("Find result: " + list.size() + " objects");
		return list;
	}

	private Criteria getCriteria(Object object) throws CoreDBException{
		this.setNull(object);
		Session session = (Session) entityManager.getDelegate();
		if(!session.isOpen()){
			session = session.getSessionFactory().openSession();
		}
		Criteria c = session.createCriteria(object.getClass());
		try {
			ClassMetadata cm = session.getSessionFactory().getClassMetadata(object.getClass());
			String id = cm.getIdentifierPropertyName();
			String[] fields = session.getSessionFactory().getClassMetadata(object.getClass()).getPropertyNames();
	
			BeanMap beanMap = new BeanMap(object);
			if(BeanUtils.getProperty(object, id) != null && !"".equals(BeanUtils.getProperty(object, id))){
				if(cm.getIdentifierType().isComponentType()){
					BeanMap IdMap = new BeanMap(beanMap.get(id));
					Iterator<?> keys = IdMap.keyIterator();
					while(keys.hasNext()){
						String key = (String)keys.next();
						if(!"class".equalsIgnoreCase(key)){
							if(IdMap.get(key) != null){
								String value = String.valueOf(IdMap.get(key));
								if(value.indexOf("-") == 0){
									value = value.substring(1);
									c = c.add(Restrictions.ne(id.concat(".").concat(key), IdMap.get(key)));
								}else{
									c = c.add(Restrictions.eq(id.concat(".").concat(key), IdMap.get(key)));
								}
							}
						}
					}
				}else{
					c = c.add(Restrictions.eq(id, beanMap.get(id)));
				}
			}
	
			for(int i = 0; i < fields.length; i++){
				if(BeanUtils.getProperty(object, fields[i]) != null){
					if(String.class.isInstance(beanMap.get(fields[i])) && beanMap.get(fields[i]) != null && !"".equalsIgnoreCase((String)beanMap.get(fields[i])))
						c = c.add(Restrictions.ilike(fields[i], (String)beanMap.get(fields[i]),MatchMode.ANYWHERE));
					else
						c = c.add(Restrictions.eq(fields[i], beanMap.get(fields[i])));
				}
			}
		}catch(Exception e){
			throw new CoreDBException(e);
		}
		return c;
	}

	public List<?> getFindCriteria(Object object, String orderBy) throws CoreDBException{
		List<?> list = null;
		try {
			this.setNull(object);
			list = getCriteria(object, orderBy).list();			
		} catch (HibernateException e) {
			log.error(e);
			throw new CoreDBException(e);
		}
		log.debug("Find result: " + list.size() + " objects");
		
		return list;
	}

	protected Criteria getCriteria(Object object, String ordenado) throws CoreDBException{
		this.setNull(object);
		Criteria criteria = getCriteria(object);
                if(ordenado.indexOf('.')!=-1){
                    String[] ords=ordenado.split("\\.");
                    int lgth=ords.length;
                    log.debug("ords:"+ordenado+" // length="+lgth);
                    for (int i = 0; i < lgth-1; i++) {
                        if(i==0) criteria=criteria.createAlias(ords[i],"alias"+i);
                        else criteria=criteria.createAlias("alias"+(i-1)+"."+ords[i],"alias"+i);
                    }
                    ordenado="alias"+(lgth-2)+"."+ords[lgth-1];
                    log.debug("Ordenado:"+ordenado);
                    criteria.addOrder(Order.asc(ordenado));
                }else
                    criteria.addOrder(Property.forName(ordenado).asc());
		return criteria;
	}

	@SuppressWarnings("unchecked")
	public void setNull(Object obj) throws CoreDBException {
		try {

			if (obj != null) {
				if(!ClassUtils.isAssignable(Map.class,obj.getClass())){
					Field fields[] = obj.getClass().getDeclaredFields();
					for (int i = 0; i < fields.length; ++i) {
						fields[i].setAccessible(true);
						if (fields[i].getType().getName().equals("java.lang.Long")) {
							Long valor = (Long) fields[i].get(obj);
							if (fields[i].get(obj) != null
									&& valor.longValue() == 0) {
								fields[i].set(obj, null);
							}
						}
						if (fields[i].getType().getName()
								.equals("java.lang.String")
								|| fields[i].getType().getName().equals(
										"java.util.Date")) {
							if (fields[i].get(obj) != null
									&& fields[i].get(obj).equals("")) {
								fields[i].set(obj, null);
							}
						}
					}
				}else{
					Map m = (Map)obj;
					Iterator<Object> it = m.keySet().iterator();
					while(it.hasNext()){
						Object key = it.next();
						if (m.get(key) != null && m.get(key).getClass().getName().equals("java.lang.Long")) {
							Long valor = (Long) m.get(key);
							if (m.get(key) != null && valor.longValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null && m.get(key).getClass().getName().equals("java.lang.Double")) {
							Double valor = (Double) m.get(key);
							if (m.get(key) != null && valor.doubleValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null && m.get(key).getClass().getName().equals("java.lang.Integer")) {
							Integer valor = (Integer) m.get(key);
							if (m.get(key) != null && valor.intValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null && (m.get(key).getClass().getName().equals("java.lang.String")
								|| m.get(key).getClass().getName().equals("java.util.Date"))) {
							if (m.get(key) != null && "".equals(m.get(key))) {
								m.put(key, null);
							}
						}
						;
					}
				}
			}
		} catch (Exception e) {
			log.error("error al setear null :" + e.getMessage());
			throw new CoreDBException(e);
		}
	}

	public List<?> getFindCriteria(Object object, String orderBy, String restringe) throws CoreDBException{
		return getCriteria(object,orderBy,restringe).list();
	}

	public List<?> getFindCriteria(Object object, int maxResults, String orderBy) throws CoreDBException{
		List<?> list = null;
		try {
			Criteria criteria = null;
			this.setNull(object);
			if (StringUtils.isEmpty(orderBy))
				criteria = getCriteria(object);
			else
				criteria = getCriteria(object, orderBy);
			criteria.setMaxResults(maxResults);
			list = criteria.list();
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
		log.debug("Find result: " + list.size() + " objects");
		return list;
	}

	protected Criteria getCriteria(Object object, String ordenado, String restringeNull) throws CoreDBException{
		Criteria criteria = getCriteria(object,ordenado);
        if(!StringUtils.isEmpty(restringeNull)){
            String[] rn=restringeNull.split(",");
            for(int i=0;i<rn.length;i++){
                criteria.add(Restrictions.isNull(rn[i]));
            }
        }
		return criteria;
	}

	public List<?> getFindCriteria(Object object, int maxResults) throws CoreDBException {
		return getFindCriteria(object,maxResults,null);
	}

	public String getIdFieldName(Class<?> clazz) throws CoreDBException{
		Session session = (Session) entityManager.getDelegate();
		return session.getSessionFactory().getClassMetadata(clazz).getIdentifierPropertyName();
	}

	public void initialize(Object object) throws CoreDBException{
		Hibernate.initialize(object);
	}

	public void initializeCollection(Collection<?> collection) throws CoreDBException{
		java.util.Iterator<?> it=collection.iterator();
        while(it.hasNext())
        	initialize(it.next());
	}

	public void setNullToSave(Object obj) throws CoreDBException {
		try {
			if (obj != null) {
				Map<String, Object> fields = ReflectionUtils.discoverFields(obj.getClass());
				Iterator<?> it = fields.keySet().iterator();
				while(it.hasNext()){
					Object key = it.next();
					Field field = (Field)fields.get(key);
					field.setAccessible(true);
					if(ClassUtils.isAssignable(GenericBean.class, field.getType())){
						GenericBean gb = (GenericBean)field.get(obj);
						if(gb != null)
							if(gb.getCodigo() == null){
								field.set(obj, null);
							}
					}
				}
			}
		} catch (Exception e) {
			log.error("error al setear null :" + e.getMessage());
			throw new CoreDBException(e);
		}
	}

	public void save(Object object) throws CoreDBException{
		log.debug("saving instance");
		setNullToSave(object);
		try {
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			HibernateTemplate hibernateTemplate = new HibernateTemplate(session.getSessionFactory());
			hibernateTemplate.saveOrUpdate(object);
			log.debug("save successful");
		} catch (Exception re) {
			log.error("save failed", re);
			throw new CoreDBException(re);
		}
	}

	public void saveCollection(Collection<?> collection) throws CoreDBException{
		java.util.Iterator<?> it=collection.iterator();
        while(it.hasNext())
            save(it.next());
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#listar(java.lang.String, java.lang.Object)
	 */
	public List<?> mapListar(String namedQuery, Object object) throws CoreDBException{
		return (List<?>)executeProcedure(namedQuery, object, RESULT_MAP, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#listar(java.lang.String, java.lang.Object, java.lang.Class)
	 */
	public List<?> listar(String namedQuery, Object object, Class<?> clazz) throws CoreDBException{
		return (List<?>)executeProcedure(namedQuery, object, RESULT_LIST, clazz);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#listar(java.lang.String, java.lang.Object)
	 */
	public List<?> listar(String namedQuery, Object object) throws CoreDBException{
		return (List<?>)executeProcedure(namedQuery, object, RESULT_LIST, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeProcedure(java.lang.String, java.lang.Object)
	 */
	public Object executeProcedure(String namedQuery, Object object) throws CoreDBException{
		return executeProcedure(namedQuery, object, RESULT_OBJECT, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeProcedure(java.lang.String, java.lang.Object, java.lang.Class)
	 */
	public Object executeProcedure(String namedQuery, Object object, Class<?> clazz) throws CoreDBException{
		return executeProcedure(namedQuery, object, RESULT_OBJECT, clazz);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeVoidProcedure(java.lang.String, java.lang.Object)
	 */
	public Object executeVoidProcedure(String namedQuery, Object object) throws CoreDBException{
		return executeProcedure(namedQuery, object, RESULT_VOID, null);
	}
	

	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeProcedure(java.lang.String, java.lang.Object, int, int, int)
	 */
	@SuppressWarnings("unchecked")
	private Object executeProcedure(String namedQuery, Object object, int ResultMode, Class<?> _clazz) throws CoreDBException{
		log.debug("[GenericDAOImpl : executeProcedure] ".concat(namedQuery));
		Object results = null;
		boolean isMap = false;
		try {
			setNull(object);
			Session session = (Session) entityManager.getDelegate();
			if(!session.isOpen()){
				session = session.getSessionFactory().openSession();
			}
			org.hibernate.Query query = session.createSQLQuery(MapUtils.getString(sqlQueryMap, namedQuery));

			if(object != null){
				Map map = null;
				Iterator<?> i = null;
					if(!ClassUtils.isAssignable(Map.class,object.getClass())){
						map = new BeanMap(object);
						i = ReflectionUtils.discoverFields(object.getClass()).keySet().iterator();
					}else{
						map = (Map)object;
						isMap = true;
						i = map.keySet().iterator();
					}
				log.debug("ClazzName : Key : IsPublic");
				while(i.hasNext()){
					Object key = i.next();
					if(!String.valueOf(key).equalsIgnoreCase("class") && !String.valueOf(key).equalsIgnoreCase("serialVersionUID")){
						Class<?> clazz = null;
						if(!isMap){
							Field field = null;
							try{
								field = object.getClass().getDeclaredField(String.valueOf(key));
							}catch(NoSuchFieldException e){
								log.debug("field don't declared ",e);
							}
							if(field != null){
								clazz = field.getType();
								log.debug(ClassUtils.getShortName(clazz) + " : " + key + " : " + ReflectHelper.isPublic(object.getClass(), field));
							}
						}else{
							clazz = map.get(key) != null ? map.get(key).getClass() : String.class;
						}
						if(clazz != null){
							if(!clazz.isAssignableFrom(Set.class)){
								if(!clazz.isAssignableFrom(List.class)){
									if(!clazz.isAssignableFrom(Map.class)){
										if(!clazz.isInterface() && GenericBean.class.isAssignableFrom(clazz.getSuperclass())){
											Map attribute = new BeanMap(map.get(key));
											Iterator<?> o = attribute.keySet().iterator();
											while(o.hasNext()){
												Object keyO = o.next();
												if(!String.valueOf(keyO).equals("class")){
													Object attributeO = attribute.get(keyO);
													query.setParameter(String.valueOf(keyO), attributeO);
												}
											}
										}else{
											Object attribute = map.get(key);
											try{
												query.setParameter(String.valueOf(key), attribute);
											}catch(Exception e){
												log.warn("can't set parameter now try add value type ["+ClassUtils.getClassFileName(clazz)+"]");
											}
										}
									}
								}
							}
						}
					}
				}
			}

			switch (ResultMode) {
				case RESULT_MAP:
					results = query.setResultTransformer(Utils.ALIAS_TO_ENTITY_ORDERED_MAP).list();
					break;
				case RESULT_LIST:
					results = _clazz != null ? query.setResultTransformer(Utils.aliasToBean(_clazz)).list() : query.list();
					break;
				case RESULT_OBJECT:
					results = _clazz != null ? query.setResultTransformer(Utils.aliasToBean(_clazz)).uniqueResult() : query.uniqueResult();
					break;
				case RESULT_VOID:
					results = String.valueOf(query.executeUpdate());
					break;
			}

			return results;
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#mapListarQuery(java.lang.String, java.lang.Object)
	 */
	public List<?> mapListarQuery(String sqlQuery, Object object) throws CoreDBException{
		return (List<?>)executeQuery(sqlQuery, object, RESULT_MAP, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#listarQuery(java.lang.String, java.lang.Object, java.lang.Class)
	 */
	public List<?> listarQuery(String sqlQuery, Object object, Class<?> clazz) throws CoreDBException{
		return (List<?>)executeQuery(sqlQuery, object, RESULT_LIST, clazz);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#listarQuery(java.lang.String, java.lang.Object)
	 */
	public List<?> listarQuery(String sqlQuery, Object object) throws CoreDBException{
		return (List<?>)executeQuery(sqlQuery, object, RESULT_LIST, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeQuery(java.lang.String, java.lang.Object)
	 */
	public Object executeQuery(String sqlQuery, Object object) throws CoreDBException{
		return executeQuery(sqlQuery, object, RESULT_OBJECT, null);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeQuery(java.lang.String, java.lang.Object, java.lang.Class)
	 */
	public Object executeQuery(String sqlQuery, Object object, Class<?> clazz) throws CoreDBException{
		return executeQuery(sqlQuery, object, RESULT_OBJECT, clazz);
	}
	
	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeVoidQuery(java.lang.String, java.lang.Object)
	 */
	public Object executeVoidQuery(String sqlQuery, Object object) throws CoreDBException{
		return executeQuery(sqlQuery, object, RESULT_VOID, null);
	}

	/* (non-Javadoc)
	 * @see com.core.model.dao.hibernate.GenericDaoImpl#executeQuery(java.lang.String, java.lang.Object, int, int, int)
	 */
	@SuppressWarnings("unchecked")
	private Object executeQuery(String sqlQuery, Object object, int ResultMode, Class<?> _clazz) throws CoreDBException{
		log.debug("[GenericDAOImpl : executeQuery] ".concat(sqlQuery));
		Object results = null;
		boolean isMap = false;
		try {
			setNull(object);
			Session session = (Session) entityManager.getDelegate();
			org.hibernate.Query query = session.createSQLQuery(sqlQuery);

			if(object != null){
				Map map = null;
				Iterator<?> i = null;
					if(!ClassUtils.isAssignable(Map.class,object.getClass())){
						map = new BeanMap(object);
						i = ReflectionUtils.discoverFields(object.getClass()).keySet().iterator();
					}else{
						map = (Map)object;
						isMap = true;
						i = map.keySet().iterator();
					}
				log.debug("ClazzName : Key : IsPublic");
				while(i.hasNext()){
					Object key = i.next();
					if(!String.valueOf(key).equalsIgnoreCase("class") && !String.valueOf(key).equalsIgnoreCase("serialVersionUID")){
						Class<?> clazz = null;
						if(!isMap){
							Field field = null;
							try{
								field = object.getClass().getDeclaredField(String.valueOf(key));
							}catch(NoSuchFieldException e){
								log.debug("field don't declared ",e);
							}
							if(field != null){
								clazz = field.getType();
								log.debug(ClassUtils.getShortName(clazz) + " : " + key + " : " + ReflectHelper.isPublic(object.getClass(), field));
							}
						}else{
							clazz = map.get(key) != null ? map.get(key).getClass() : String.class;
						}
						if(clazz != null){
							if(!clazz.isAssignableFrom(Set.class)){
								if(!clazz.isAssignableFrom(List.class)){
									if(!clazz.isAssignableFrom(Map.class)){
										if(!clazz.isInterface() && GenericBean.class.isAssignableFrom(clazz.getSuperclass())){
											Map attribute = new BeanMap(map.get(key));
											Iterator<?> o = attribute.keySet().iterator();
											while(o.hasNext()){
												Object keyO = o.next();
												if(!String.valueOf(keyO).equals("class")){
													Object attributeO = attribute.get(keyO);
													query.setParameter(String.valueOf(keyO), attributeO);
												}
											}
										}else{
											Object attribute = map.get(key);
											try{
												query.setParameter(String.valueOf(key), attribute);
											}catch(Exception e){
												log.warn("can't set parameter now try add value type ["+ClassUtils.getClassFileName(clazz)+"]");
												if(clazz.isAssignableFrom(String.class))
													query.setString(String.valueOf(key), (String)attribute);
												if(clazz.isAssignableFrom(Long.class)){
													if(attribute == null)
														query.setString(String.valueOf(key), (String)attribute);
													else
														query.setLong(String.valueOf(key), ((Long)attribute).longValue());
												}
												if(clazz.isAssignableFrom(Integer.class)){
													if(attribute == null)
														query.setString(String.valueOf(key), (String)attribute);
													else
														query.setInteger(String.valueOf(key), ((Integer)attribute).intValue());
												}
												if(clazz.isAssignableFrom(Date.class))
													query.setDate(String.valueOf(key), (Date)attribute);
												if(clazz.isAssignableFrom(BigDecimal.class))
													query.setBigDecimal(String.valueOf(key), (BigDecimal)attribute);
												if(clazz.isAssignableFrom(BigInteger.class))
													query.setBigInteger(String.valueOf(key), (BigInteger)attribute);
												if(clazz.isAssignableFrom(byte[].class))
													query.setBinary(String.valueOf(key), (byte[])attribute);
												if(clazz.isAssignableFrom(Boolean.class))
													query.setBoolean(String.valueOf(key), ((Boolean)attribute).booleanValue());
												if(clazz.isAssignableFrom(Byte.class))
													query.setByte(String.valueOf(key), ((Byte)attribute).byteValue());
												if(clazz.isAssignableFrom(Calendar.class))
													query.setCalendar(String.valueOf(key), (Calendar)attribute);
												if(clazz.isAssignableFrom(Character.class))
													query.setCharacter(String.valueOf(key), ((Character)attribute).charValue());
												if(clazz.isAssignableFrom(Double.class))
													query.setDouble(String.valueOf(key), ((Double)attribute).doubleValue());
												if(clazz.isAssignableFrom(Serializable.class))
													query.setSerializable(String.valueOf(key), (Serializable)attribute);
												if(clazz.isAssignableFrom(Short.class))
													query.setShort(String.valueOf(key), ((Short)attribute).shortValue());
												if(clazz.isAssignableFrom(Timestamp.class))
													query.setTimestamp(String.valueOf(key), (Timestamp)attribute);
											}
										}
									}
								}
							}
						}
					}
				}
				query.setProperties(object);
			}

			switch (ResultMode) {
				case RESULT_MAP:
					results = query.setResultTransformer(Utils.ALIAS_TO_ENTITY_ORDERED_MAP).list();
					break;
				case RESULT_LIST:
					results = _clazz != null ? query.setResultTransformer(Utils.aliasToBean(_clazz)).list() : query.list();
					break;
				case RESULT_OBJECT:
					results = _clazz != null ? query.setResultTransformer(Utils.aliasToBean(_clazz)).uniqueResult() : query.uniqueResult();
					break;
				case RESULT_VOID:
					results = String.valueOf(query.executeUpdate());
					break;
			}

			return results;
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	/**
	 * Method to execute procedures and returns values
	 * this method receive and map parameter with in parameters
	 * and out map with instances of classes to return for example
	 * 
	 * Map mapIN = new OrderedMap();
	 * 
	 * Map mapOUT = new HashMap();
	 * mapOUT.put("1", new String());
	 * mapOUT.put("13", new Long(0));
	 * 
	 * @param namedQuery
	 * @param mapIn
	 * @param mapOut
	 * @return Map same mapOut but with values filled
	 * @throws CoreDBException
	 */
	@SuppressWarnings("unchecked")
	public Map executeProcedureOut(String namedQuery, Map mapIn, Map mapOut) throws CoreDBException{
		log.debug("[GenericDAOImpl : executeProcedureOut] ".concat(namedQuery));
		Map results = mapOut;
		try {
			setNull(mapIn);
			Session session = (Session) entityManager.getDelegate();
			org.hibernate.Query query = session.getNamedQuery(MapUtils.getString(sqlQueryMap, namedQuery));

			CallableStatement cs = getConnection().prepareCall(query.getQueryString());

			if(mapIn != null){
				Iterator<?> i = mapIn.keySet().iterator();;

				log.debug("ClazzName : Key : IsPublic");
				int index = 1;
				while(i.hasNext()){
					Object key = i.next();
					Class<?> clazz = mapIn.get(key) != null ? mapIn.get(key).getClass() : String.class;
					if(clazz != null){
						Object attribute = mapIn.get(key);
						if(attribute == null){
							cs.setNull(index, Types.VARCHAR);
						}else{
							if(clazz.isAssignableFrom(String.class))
								cs.setString(index, (String)attribute);
							if(clazz.isAssignableFrom(Long.class))
								cs.setLong(index, ((Long)attribute).longValue());
							if(clazz.isAssignableFrom(Integer.class))
								cs.setInt(index, ((Integer)attribute).intValue());
							if(clazz.isAssignableFrom(Date.class))
								cs.setDate(index, new java.sql.Date(((Date)attribute).getTime()));
							if(clazz.isAssignableFrom(BigDecimal.class))
								cs.setBigDecimal(index, (BigDecimal)attribute);
							if(clazz.isAssignableFrom(byte[].class))
								cs.setBytes(index, (byte[])attribute);
							if(clazz.isAssignableFrom(Boolean.class))
								cs.setBoolean(index, ((Boolean)attribute).booleanValue());
							if(clazz.isAssignableFrom(Byte.class))
								cs.setByte(index, ((Byte)attribute).byteValue());
							if(clazz.isAssignableFrom(Double.class))
								cs.setDouble(index, ((Double)attribute).doubleValue());
							if(clazz.isAssignableFrom(Short.class))
								cs.setShort(index, ((Short)attribute).shortValue());
							if(clazz.isAssignableFrom(Timestamp.class))
								cs.setTimestamp(index, (Timestamp)attribute);
						}
					}
					index++;
				}
			}

			if(mapOut != null){
				Iterator<?> i = mapOut.keySet().iterator();;

				log.debug("ClazzName : Key : IsPublic");
				while(i.hasNext()){
					Object key = i.next();
					Class<?> clazz = mapOut.get(key) != null ? mapOut.get(key).getClass() : String.class;
					if(clazz != null){
						if(clazz.isAssignableFrom(String.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.VARCHAR);
						if(clazz.isAssignableFrom(Long.class) || clazz.isAssignableFrom(Integer.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.INTEGER);
						if(clazz.isAssignableFrom(Date.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.DATE);
						if(clazz.isAssignableFrom(BigDecimal.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.DECIMAL);
						if(clazz.isAssignableFrom(byte[].class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.BLOB);
						if(clazz.isAssignableFrom(Boolean.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.BOOLEAN);
						if(clazz.isAssignableFrom(Byte.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.BINARY);
						if(clazz.isAssignableFrom(Double.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.DOUBLE);
						if(clazz.isAssignableFrom(Short.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.TINYINT);
						if(clazz.isAssignableFrom(Timestamp.class))
							cs.registerOutParameter(Integer.parseInt((String)key), Types.TIMESTAMP);
					}
				}
			}
			cs.execute();

			if(mapOut != null){
				Iterator<?> i = mapOut.keySet().iterator();;

				log.debug("ClazzName : Key : IsPublic");
				while(i.hasNext()){
					Object key = i.next();
					Class<?> clazz = mapOut.get(key) != null ? mapOut.get(key).getClass() : String.class;
					if(clazz != null){
						if(clazz.isAssignableFrom(String.class))
							results.put(String.valueOf(key), cs.getString(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Long.class))
							results.put(String.valueOf(key), cs.getLong(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Integer.class))
							results.put(String.valueOf(key), cs.getInt(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Date.class))
							results.put(String.valueOf(key), cs.getDate(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(BigDecimal.class) || clazz.isAssignableFrom(Double.class))
							results.put(String.valueOf(key), cs.getDouble(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(byte[].class))
							results.put(String.valueOf(key), cs.getBlob(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Boolean.class))
							results.put(String.valueOf(key), cs.getBoolean(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Byte.class))
							results.put(String.valueOf(key), cs.getByte(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Short.class))
							results.put(String.valueOf(key), cs.getShort(Integer.parseInt((String)key)));
						if(clazz.isAssignableFrom(Timestamp.class))
							results.put(String.valueOf(key), cs.getTimestamp(Integer.parseInt((String)key)));
					}
				}
			}

			return results;
		} catch (Exception e) {
			log.error(e);
			throw new CoreDBException(e);
		}
	}

	public List<String> getXmlFiles(){
		return xmlFiles;
	}

	public void setXmlFiles(List<String> xmlFiles){
		this.xmlFiles = xmlFiles;
	}
}